package com.zp.self.module.level_1_问道.X_JDK8新功能.Stream;

import com.google.common.collect.Lists;
import com.zp.self.module.level_1_问道.X_JDK8新功能.Stream.entity.Employee;
import org.junit.Test;

import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author By ZengPeng
 * @Description  一个流只会有一个终止操作。 Stream只有遇到终止操作，它的源才开始执行遍历操作。注意，在这之后，这个流就不能再使用了。
 *          1、查找与匹配
 *          2、规约
 *          3、收集
 * @date in  2021/3/2 10:47
 * @Modified By
 */
public class c终止操作 {
    //-------------------------------------------1、查找与匹配  【感觉还是算中间操作啊】-------------------------------------------
    //  allMatch(Predicate p)，传入一个断言型函数，检查是否匹配所有元素
    //  anyMatch( (Predicate p) )，检查是否匹配任意一个元素
    //  noneMatch(Predicate p)，检查是否没有匹配的元素，如果都不匹配，则返回 true
    //  findFirst()，返回第一个元素
    //  findAny()，返回任意一个元素
    //  count()，返回流中的元素总个数
    //  max(Comparator c)，按给定的规则排序后，返回最大的元素
    //  min(Comparator c)，按给定的规则排序后，返回最小的元素
    //  forEach(Consumer c)，迭代遍历元素（内部迭代）




    //-------------------------------------------2、规约  -------------------------------------------
    //规约就是 reduce ，把数据集合到一起
    @Test
    public void statute(){
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
        Integer res1 =stream.reduce((x, y) -> x + y).get();
        System.out.println(res1);
        // 结果：21


        //2.3.1、 非并行规约
        Integer[] arr = {1,2,3,4,5,6};
        ArrayList<Integer> res = Stream.of(arr).reduce(Lists.newArrayList(0),
                (l, e) -> {
                    l.add(e);
                    return l;
                },
                (l, c) -> {
                    //结果不会打印这句话，说明第三个参数没有起作用
                    System.out.println("combiner");
                    l.addAll(c);
                    return l;
                });
        System.out.println(res);
        // [0, 1, 2, 3, 4, 5, 6]

        //2.3.2、并行规约
        //  这里就要说明，这个 identity 初始值了。它是在每次执行 combiner 的时候，都会把 identity 累加上。
        Integer res4 = Stream.of(1,2,3,4).parallel().reduce(1,
                (s,e) -> s + e,
                (sum, s) -> sum + s);
        System.out.println(res4); // 结果：14

        //具体执行几次 combiner ，可以通过以下方式计算出来 。（ c 并不能代表有几个执行子任务）
        AtomicInteger c = new AtomicInteger(0);
        Integer res5 = Stream.of(1,2,3,4).parallel().reduce(1,
                (s,e) -> s + e,
                (sum, s) -> {c.getAndIncrement(); return sum + s;});
        System.out.println(c); //3
        System.out.println(res5); //14
        //c 为 3 代表执行了 3 次 combiner ，最后计算总结果时，还会再加一次初始值，所以结果为：
        //(1+2+3+4) + (3+1) * 1 = 14
    }

    @Test
    //有问题啊
    public void test(){
        AtomicInteger count = new AtomicInteger(0);
        int length = 100;
        Integer[] arr1 = new Integer[length];
        for (int i = 0; i < length; i++) {
            arr1[i] = i + 1;
        }
        Integer res5 = Stream.of(arr1).parallel().reduce(2,
                (s,e) -> s + e,
                (sum, s) -> {count.getAndIncrement(); return sum + s;});
        System.out.println(count.get()); //15
        System.out.println(res5); //5082
    }
    @Test
    //非也，其实是我们的用法姿势错了。可以看下源码中对 identity 的说明。
    public void test2(){
        //其他都不变，只有 identity 由 2 改为 0
        AtomicInteger count = new AtomicInteger(0);
        int length = 100;
        Integer[] arr1 = new Integer[length];
        for (int i = 0; i < length; i++) {
            arr1[i] = i + 1;
        }
        Integer res5 = Stream.of(arr1).parallel().reduce(0,
                (s,e) -> s + e,
                (sum, s) -> {count.getAndIncrement(); return sum + s;});
        System.out.println(count.get()); //15
        System.out.println(res5); //5050
    }

    @Test
    //当然，只要保证 identity 不影响这个恒等式就行。
    //比如，对于 set 集合会自动去重，这种情况下，也可以使用并行计算，
    public void test3(){
        //初始化一个set，然后把stream流的元素添加到set中，
//需要注意：用并行的方式，这个set集合必须是线程安全的。否则会报错ConcurrentModificationException
//        Set<Integer> res3 = Stream.of(1, 2, 3, 4).parallel().reduce(
//                Collections.synchronizedSet(Sets.newHashSet(10),
//                (l, e) -> {
//                    l.add(e);
//                    return l;
//                },
//                (l, c) -> {
//                    l.addAll(c);
//                    return l;
//                }));
//        System.out.println(res3);
    }




    //-------------------------------------------3、收集  -------------------------------------------
    //收集操作，可以把流收集到 List，Set，Map等中。而且，Collectors 类中提供了很多静态方法，方便的创建收集器供我们使用。
    @Test
    public void collect(){
        ArrayList<Employee> list = new ArrayList<>();
        list.add(new Employee("张三", 3000));
        list.add(new Employee("李四", 5000));
        list.add(new Employee("王五", 4000));
        list.add(new Employee("赵六", 4500));

        //把所有员工的姓名收集到list中
        list.stream()
                .map(Employee::getName)
                .collect(Collectors.toList())
                .forEach(System.out::println);

        //求出所有员工的薪资平均值
        Double average = list.stream()
                .collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println(average);
    }
}
